﻿using Common.Utility;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Formatters;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Api.Filter
{
    public class RequestResponseLoggingMiddleware
    {
        private readonly RequestDelegate _next;
        private RequestResponseLog _logInfo;

        public RequestResponseLoggingMiddleware(RequestDelegate next)
        {
            _next = next;
        }


        public async Task Invoke(HttpContext context)
        {
            try
            {
                if (SkipLogging(context))
                {
                    await _next(context);
                }
                else
                {
                    DateTime StartTime = DateTime.Now;
                    //过滤非Api的请求
                    var request = context.Request;
                    if (request.Path.Value.ToLower().Contains("api"))
                    {
                        var requestIp = GetRemoteIp(context);
                        _logInfo = new RequestResponseLog()
                        {
                            IP = requestIp,
                            Url = request.Path,
                            Method = request.Method,
                            ExcuteStartTime = DateTime.Now,
                        };

                        // 获取请求body内容
                        if (request.Method.ToLower().Equals("post"))
                        {
                            //启用读取request
                            request.EnableBuffering();

                            //设置当前流中的位置为起点
                            request.Body.Seek(0, SeekOrigin.Begin);

                            //把请求body流转换成字符串
                            _logInfo.RequestBody = await new StreamReader(request.Body).ReadToEndAsync();

                            request.Body.Seek(0, SeekOrigin.Begin);
                        }
                        else if (request.Method.ToLower().Equals("get"))
                        {
                            _logInfo.RequestBody = request.QueryString.Value;
                        }

                        var originalBodyStream = context.Response.Body;
                        using (var responseBody = new MemoryStream())
                        {
                            context.Response.Body = responseBody;
                            await _next(context);

                            //返回结果                        
                            var response = context.Response;

                            //设置当前流中的位置为起点
                            responseBody.Seek(0, SeekOrigin.Begin);

                            var responesInfo = await new StreamReader(responseBody).ReadToEndAsync();

                            //设置当前流中的位置为起点
                            responseBody.Seek(0, SeekOrigin.Begin);

                            // 编码转换，解决中文乱码
                            responesInfo = Regex.Unescape(responesInfo);

                            //返回参数
                            _logInfo.ResponseBody = responesInfo;
                            _logInfo.ExcuteEndTime = DateTime.Now;

                            await responseBody.CopyToAsync(originalBodyStream);
                            context.Response.Body = originalBodyStream;
                        }
                        // 操作日志持久化缓存到mongo
                        LogHelper.Info(_logInfo.ToString());
                    }
                    else
                    {
                        await _next(context);
                    }
                }
            }
            catch (Exception ex)
            {
                LogHelper.Error(ex);
                await HandleExceptionAsync(context, -1, "未知错误");
            }

        }

        /// <summary>
        /// 根据文件类型获取数据编码类型
        /// </summary>
        /// <param name="contentType">文件类型</param>
        /// <returns></returns>
        private Encoding GetEncoding(string contentType)
        {
            var mediaType = contentType == null ? default : new MediaType(contentType);
            var encoding = mediaType.Encoding;
            if (encoding == null)
            {
                encoding = Encoding.UTF8;
            }
            return encoding;
        }

        /// <summary>
        /// 获取客户端Ip
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public static string GetRemoteIp(HttpContext context)
        {
            var remoteIp = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
            if (string.IsNullOrEmpty(remoteIp))
            {
                remoteIp = context.Connection.RemoteIpAddress.ToString();
                if (remoteIp == "::1")
                {
                    remoteIp = "127.0.0.1";
                }
                return remoteIp;
            }
            return remoteIp;
        }

        /// <summary>
        /// 返回请求信息
        /// </summary>
        /// <param name="context">上下文</param>
        /// <param name="code">返回编码</param>
        /// <param name="info">返回信息</param>
        /// <returns></returns>
        private static Task HandleExceptionAsync(HttpContext context, int code, string info)
        {
            object operateStatus = new
            {
                code,
                info
            };
            var result = JsonConvert.SerializeObject(operateStatus);
            context.Response.ContentType = "application/json;charset=utf-8";
            return context.Response.WriteAsync(result);
        }

        /// <summary>
        /// 判断类和方法头上的特性是否要进行Action拦截
        /// </summary>
        /// <param name="actionContext"></param>
        /// <returns></returns>
        private static bool SkipLogging(HttpContext context)
        {
            var endpoint = context.GetEndpoint();
            if (endpoint != null)
            {
                var attr = endpoint.Metadata.GetMetadata<NoLogAttribute>();
                if (attr != null)
                {
                    return true;
                }
            }
            return false;
        }
    }

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)]
    public class NoLogAttribute : Attribute
    {
    }

    public static class RequestResponseLoggingMiddlewareExtensions
    {
        public static IApplicationBuilder UseRequestResponseLogging(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<RequestResponseLoggingMiddleware>();
        }
    }

    public class RequestResponseLog
    {
        public string Url { get; set; }
        public IDictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
        public string Method { get; set; }
        public string RequestBody { get; set; }
        public string ResponseBody { get; set; }
        public DateTime ExcuteStartTime { get; set; }
        public DateTime ExcuteEndTime { get; set; }
        public string IP { get; set; }
        public override string ToString()
        {
            string headers = "[" + string.Join(",", Headers.Select(i => "{" + $"{i.Key}:{i.Value}" + "}")) + "]";
            return $"IP: {IP},Url: {Url},ExcuteStartTime: {ExcuteStartTime.ToString("yyyy-MM-dd HH:mm:ss.fff")},ExcuteEndTime: {ExcuteEndTime.ToString("yyyy-MM-dd HH:mm:ss.fff")},Headers: {headers},Method: {Method},RequestBody: {RequestBody},ResponseBody: {ResponseBody}";
        }
    }
}
